winsafe\gui\native_controls/
tab.rs1use std::any::Any;
2use std::marker::PhantomPinned;
3use std::pin::Pin;
4use std::sync::Arc;
5
6use crate::co;
7use crate::decl::*;
8use crate::guard::*;
9use crate::gui::{collections::*, events::*, privs::*, *};
10use crate::msg::*;
11use crate::prelude::*;
12
13struct TabObj {
14 base: BaseCtrl,
15 events: TabEvents,
16 children: Vec<(String, Box<dyn AsRef<WindowControl>>)>, _pin: PhantomPinned,
18}
19
20native_ctrl! { Tab: TabObj => TabEvents;
21 }
25
26impl Tab {
27 #[must_use]
35 pub fn new(parent: &(impl GuiParent + 'static), opts: TabOpts) -> Self {
36 let mut opts = opts;
37 let ctrl_id = auto_id::set_if_zero(opts.ctrl_id);
38 let children = opts.items.drain(..).collect::<Vec<_>>();
39
40 let new_self = Self(Arc::pin(TabObj {
41 base: BaseCtrl::new(ctrl_id),
42 events: TabEvents::new(parent, ctrl_id),
43 children,
44 _pin: PhantomPinned,
45 }));
46
47 let self2 = new_self.clone();
48 let parent2 = parent.clone();
49 parent
50 .as_ref()
51 .before_on()
52 .wm(parent.as_ref().wnd_ty().creation_msg(), move |_| {
53 self2.0.base.create_window(
54 opts.window_ex_style,
55 "SysTabControl32",
56 None,
57 opts.window_style | opts.control_style.into(),
58 opts.position.into(),
59 opts.size.into(),
60 &parent2,
61 );
62 ui_font::set(self2.hwnd());
63 if opts.control_ex_style != co::TCS_EX::NoValue {
64 self2.set_extended_style(true, opts.control_ex_style);
65 }
66 self2.0.children.iter().for_each(|(text, _)| unsafe {
67 self2.items().add(text); });
69 self2.display_tab(0); parent2
71 .as_ref()
72 .add_to_layout(self2.hwnd(), opts.resize_behavior);
73 Ok(0) });
75
76 new_self.default_message_handlers(parent);
77 new_self
78 }
79
80 #[must_use]
88 pub fn new_dlg(
89 parent: &(impl GuiParent + 'static),
90 ctrl_id: u16,
91 resize_behavior: (Horz, Vert),
92 items: Vec<(String, Box<dyn AsRef<WindowControl>>)>,
93 ) -> Self {
94 let new_self = Self(Arc::pin(TabObj {
95 base: BaseCtrl::new(ctrl_id),
96 events: TabEvents::new(parent, ctrl_id),
97 children: items,
98 _pin: PhantomPinned,
99 }));
100
101 let self2 = new_self.clone();
102 let parent2 = parent.clone();
103 parent.as_ref().before_on().wm_init_dialog(move |_| {
104 self2.0.base.assign_dlg(&parent2);
105 self2.0.children.iter().for_each(|(text, _)| unsafe {
106 self2.items().add(text); });
108 self2.display_tab(0); parent2
110 .as_ref()
111 .add_to_layout(self2.hwnd(), resize_behavior);
112 Ok(true) });
114
115 new_self.default_message_handlers(parent);
116 new_self
117 }
118
119 fn default_message_handlers(&self, parent: &impl AsRef<BaseWnd>) {
120 let self2 = self.clone();
121 parent
122 .as_ref()
123 .before_on()
124 .wm_notify(self.ctrl_id(), co::TCN::SELCHANGE, move |_| {
125 if let Some(sel_item) = self2.items().selected() {
126 self2.display_tab(sel_item.index());
127 }
128 Ok(0) });
130
131 let self2 = self.clone();
132 parent.as_ref().after_on().wm_destroy(move || {
133 unsafe {
134 self2.hwnd().SendMessage(tcm::GetImageList {}).map(|h| {
135 self2
136 .hwnd()
137 .SendMessage(tcm::SetImageList { himagelist: None }); let _ = ImageListDestroyGuard::new(h); });
140 }
141 Ok(())
142 });
143 }
144
145 fn display_tab(&self, index: u32) {
146 self.0
147 .children
148 .iter()
149 .enumerate()
150 .filter(|(i, _)| *i != index as usize)
151 .for_each(|(_, (_, item))| {
152 item.as_ref().as_ref().hwnd().ShowWindow(co::SW::HIDE); });
154
155 if let Some((_, item)) = self.0.children.get(index as usize) {
156 let mut rc = self
157 .hwnd()
158 .GetParent()
159 .expect(DONTFAIL)
160 .ScreenToClientRc(self.hwnd().GetWindowRect().expect(DONTFAIL))
161 .expect(DONTFAIL);
162
163 unsafe {
164 self.hwnd().SendMessage(tcm::AdjustRect {
165 display_rect: false,
166 rect: &mut rc, });
168 }
169
170 item.as_ref()
171 .as_ref()
172 .hwnd()
173 .SetWindowPos(
174 HwndPlace::None,
175 POINT::with(rc.left, rc.top),
176 SIZE::with(rc.right - rc.left, rc.bottom - rc.top),
177 co::SWP::NOZORDER | co::SWP::SHOWWINDOW, )
179 .expect(DONTFAIL);
180 }
181 }
182
183 #[must_use]
192 pub fn image_list(&self) -> HrResult<HIMAGELIST> {
193 match unsafe { self.hwnd().SendMessage(tcm::GetImageList {}) } {
194 Some(h) => Ok(h), None => {
196 let h = HIMAGELIST::Create(SIZE::with(16, 16), co::ILC::COLOR32, 1, 1)?.leak();
198 unsafe {
199 self.hwnd()
200 .SendMessage(tcm::SetImageList { himagelist: Some(h.raw_copy()) });
201 }
202 Ok(h)
203 },
204 }
205 }
206
207 #[must_use]
209 pub const fn items(&self) -> TabItems {
210 TabItems::new(self)
211 }
212
213 pub fn set_extended_style(&self, set: bool, ex_style: co::TCS_EX) {
216 unsafe {
217 self.hwnd().SendMessage(tcm::SetExtendedStyle {
218 mask: ex_style,
219 style: if set { ex_style } else { co::TCS_EX::NoValue },
220 });
221 }
222 }
223}
224
225pub struct TabOpts {
228 pub position: (i32, i32),
234 pub size: (i32, i32),
239 pub control_style: co::TCS,
244 pub control_ex_style: co::TCS_EX,
249 pub window_style: co::WS,
254 pub window_ex_style: co::WS_EX,
259
260 pub ctrl_id: u16,
264 pub resize_behavior: (Horz, Vert),
269
270 pub items: Vec<(String, Box<dyn AsRef<WindowControl>>)>,
280}
281
282impl Default for TabOpts {
283 fn default() -> Self {
284 Self {
285 position: dpi(0, 0),
286 size: dpi(80, 50),
287 control_style: co::TCS::NoValue,
288 control_ex_style: co::TCS_EX::NoValue,
289 window_style: co::WS::CHILD | co::WS::GROUP | co::WS::TABSTOP | co::WS::VISIBLE,
290 window_ex_style: co::WS_EX::LEFT,
291 ctrl_id: 0,
292 resize_behavior: (Horz::None, Vert::None),
293 items: Vec::new(),
294 }
295 }
296}